package cn.seqdata.diff;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public enum Joiner {
	inner {
		@Override
		public <ID, L, R> List<JoinValue<ID, L, R>> join(Collection<? extends L> lefts, Collection<? extends R> rights,
				Function<L, ID> lkey, Function<R, ID> rkey) {
			List<JoinValue<ID, L, R>> results = new ArrayList<>();

			Map<ID, R> rmap = rights.stream()
				.collect(Collectors.toMap(rkey, Function.identity()));

			for (L left : lefts) {
				ID id = lkey.apply(left);
				R right = rmap.remove(id);

				if (null != right) {
					JoinValue<ID, L, R> result = new JoinValue<ID, L, R>(id);

					result.setLeft(left);
					result.setRight(right);

					results.add(result);
				}
			}

			return results;
		}
	},
	outer {
		@Override
		public <ID, L, R> List<JoinValue<ID, L, R>> join(Collection<? extends L> lefts, Collection<? extends R> rights,
				Function<L, ID> lkey, Function<R, ID> rkey) {
			List<JoinValue<ID, L, R>> results = new ArrayList<>();

			Map<ID, R> rmap = rights.stream()
				.collect(Collectors.toMap(rkey, Function.identity()));

			for (L left : lefts) {
				ID id = lkey.apply(left);
				JoinValue<ID, L, R> result = new JoinValue<ID, L, R>(id);

				result.setLeft(left);
				result.setRight(rmap.remove(id));

				results.add(result);
			}

			for (Map.Entry<ID, R> entry : rmap.entrySet()) {
				JoinValue<ID, L, R> result = new JoinValue<ID, L, R>(entry.getKey());

				result.setRight(entry.getValue());

				results.add(result);
			}

			return results;
		}
	},
	left {
		@Override
		public <ID, L, R> List<JoinValue<ID, L, R>> join(Collection<? extends L> lefts, Collection<? extends R> rights,
				Function<L, ID> lkey, Function<R, ID> rkey) {
			List<JoinValue<ID, L, R>> results = new ArrayList<>();

			Map<ID, R> rmap = rights.stream()
				.collect(Collectors.toMap(rkey, Function.identity()));

			for (L left : lefts) {
				ID id = lkey.apply(left);
				JoinValue<ID, L, R> result = new JoinValue<ID, L, R>(id);

				result.setLeft(left);
				result.setRight(rmap.remove(id));

				results.add(result);
			}

			return results;
		}
	};

	public <ID, L, R> List<JoinValue<ID, L, R>> join(Collection<? extends L> lefts, Collection<? extends R> rights,
			Function<L, ID> lkey, Function<R, ID> rkey) {
		throw new UnsupportedOperationException();
	}
}
